// -*- c++ -*-
/*
 * C++ <ostream> header
 *
 * This file is part of PanicOS.
 *
 * PanicOS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PanicOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PanicOS.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef _LIBCPP_OSTREAM
#define _LIBCPP_OSTREAM

#include <ios>
#include <iosfwd>

namespace std {
	template <class CharT, class Traits>
	class basic_ostream : virtual public std::basic_ios<CharT, Traits> {
	public:
		typedef CharT char_type;
		typedef Traits traits_type;
		typedef typename Traits::int_type int_type;
		typedef typename Traits::pos_type pos_type;
		typedef typename Traits::off_type off_type;

		explicit basic_ostream(std::basic_streambuf<CharT, Traits> *sb)
			: std::basic_ios<CharT, Traits>(sb) {}
		virtual ~basic_ostream() {}

		// formatted output
		basic_ostream &operator<<(short value) {
			return operator<<(static_cast<long long>(value));
		}

		basic_ostream &operator<<(unsigned short value) {
			return operator<<(static_cast<unsigned long long>(value));
		}

		basic_ostream &operator<<(int value) {
			return operator<<(static_cast<long long>(value));
		}

		basic_ostream &operator<<(unsigned int value) {
			return operator<<(static_cast<unsigned long long>(value));
		}

		basic_ostream &operator<<(long value) {
			return operator<<(static_cast<long long>(value));
		}

		basic_ostream &operator<<(unsigned long value) {
			return operator<<(static_cast<unsigned long long>(value));
		}

		basic_ostream &operator<<(long long value) {
			if (value == 0) {
				put('0');
				return *this;
			}
			if (basic_ios<CharT, Traits>::format_flag & ios_base::dec) {
				char_type buf[20];
				int i = 0, base = 10;
				bool neg = (value < 0);
				if (neg) {
					value = -value;
				}
				while (value) {
					buf[i++] = value % base + '0';
					value /= base;
				}
				if (neg) {
					put('-');
				}
				while (i--) {
					put(buf[i]);
				}
			} else if (basic_ios<CharT, Traits>::format_flag & ios_base::hex) {
				char_type buf[20];
				int i = 0, base = 16;
				while (value) {
					unsigned int dig = value % base;
					buf[i++] = (dig >= 10) ? (dig - 10 + 'a') : (dig + '0');
					value /= base;
				}
				while (i--) {
					put(buf[i]);
				}
			}
			return *this;
		}

		basic_ostream &operator<<(unsigned long long value) {
			if (value == 0) {
				put('0');
				return *this;
			}
			if (basic_ios<CharT, Traits>::format_flag & ios_base::dec) {
				char_type buf[20];
				int i = 0, base = 10;
				while (value) {
					buf[i++] = value % base + '0';
					value /= base;
				}
				while (i--) {
					put(buf[i]);
				}
			} else if (basic_ios<CharT, Traits>::format_flag & ios_base::hex) {
				char_type buf[20];
				int i = 0, base = 16;
				while (value) {
					unsigned int dig = value % base;
					buf[i++] = (dig >= 10) ? (dig - 10 + 'a') : (dig + '0');
					value /= base;
				}
				while (i--) {
					put(buf[i]);
				}
			}
			return *this;
		}

		basic_ostream &operator<<(bool value) {
			if (value) {
				write("true", 4);
			} else {
				write("false", 5);
			}
			return *this;
		}

		basic_ostream &operator<<(const void *value) {
			return operator<<(reinterpret_cast<unsigned long>(value));
		}

		basic_ostream &operator<<(std::nullptr_t) {
			write("nullptr", 7);
			return *this;
		}

		basic_ostream &operator<<(std::basic_streambuf<CharT, Traits> *sb) {
			return *this;
		}

		basic_ostream &operator<<(std::ios_base &(*func)(std::ios_base &io)) {
			func(*this);
			return *this;
		}

		basic_ostream &
		operator<<(std::basic_ios<CharT, Traits> &(*func)(std::basic_ios<CharT, Traits> &io)) {
			func(*this);
			return *this;
		}

		basic_ostream &
		operator<<(std::basic_ostream<CharT, Traits> &(*func)(std::basic_ostream<CharT, Traits> &io)
		) {
			func(*this);
			return *this;
		}

		// unformatted output
		basic_ostream &put(char_type ch) {
			std::basic_ios<CharT, Traits>::strbuf->sputc(ch);
			return *this;
		}

		basic_ostream &write(const char_type *s, std::streamsize count) {
			std::basic_ios<CharT, Traits>::strbuf->sputn(s, count);
			return *this;
		}
	};

	using ostream = basic_ostream<char>;

	// basic_ostream and character
	template <class CharT, class Traits>
	basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &os, CharT ch) {
		os.put(ch);
		return os;
	}

	template <class CharT, class Traits>
	basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &os, char ch) {
		os.put(ch);
		return os;
	}

	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, char ch) {
		os.put(ch);
		return os;
	}

	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, signed char ch) {
		os.put(ch);
		return os;
	}

	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, unsigned char ch) {
		os.put(ch);
		return os;
	}

	// basic_ostream and character array
	template <class CharT, class Traits>
	basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &os, const CharT *s) {
		os.write(s, Traits::length(s));
		return os;
	}

	template <class CharT, class Traits>
	basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &os, const char *s) {
		os.write(s, Traits::length(s));
		return os;
	}

	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, const char *s) {
		os.write(s, Traits::length(s));
		return os;
	}

	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, const signed char *s) {
		os.write(
			reinterpret_cast<const char *>(s), Traits::length(reinterpret_cast<const char *>(s))
		);
		return os;
	}

	template <class Traits>
	basic_ostream<char, Traits> &
	operator<<(basic_ostream<char, Traits> &os, const unsigned char *s) {
		os.write(
			reinterpret_cast<const char *>(s), Traits::length(reinterpret_cast<const char *>(s))
		);
		return os;
	}

	// deleted overloads
	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, wchar_t ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, char8_t ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, char16_t ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &operator<<(basic_ostream<char, Traits> &os, char32_t ch) = delete;
	template <class Traits>
	basic_ostream<wchar_t, Traits> &
	operator<<(basic_ostream<wchar_t, Traits> &os, char8_t ch) = delete;
	template <class Traits>
	basic_ostream<wchar_t, Traits> &
	operator<<(basic_ostream<wchar_t, Traits> &os, char16_t ch) = delete;
	template <class Traits>
	basic_ostream<wchar_t, Traits> &
	operator<<(basic_ostream<wchar_t, Traits> &os, char32_t ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &
	operator<<(basic_ostream<char, Traits> &os, const wchar_t *ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &
	operator<<(basic_ostream<char, Traits> &os, const char8_t *ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &
	operator<<(basic_ostream<char, Traits> &os, const char16_t *ch) = delete;
	template <class Traits>
	basic_ostream<char, Traits> &
	operator<<(basic_ostream<char, Traits> &os, const char32_t *ch) = delete;
	template <class Traits>
	basic_ostream<wchar_t, Traits> &
	operator<<(basic_ostream<wchar_t, Traits> &os, const char8_t *ch) = delete;
	template <class Traits>
	basic_ostream<wchar_t, Traits> &
	operator<<(basic_ostream<wchar_t, Traits> &os, const char16_t *ch) = delete;
	template <class Traits>
	basic_ostream<wchar_t, Traits> &
	operator<<(basic_ostream<wchar_t, Traits> &os, const char32_t *ch) = delete;
	// manipulators
	template <class CharT, class Traits>
	std::basic_ostream<CharT, Traits> &endl(std::basic_ostream<CharT, Traits> &os) {
		os.put('\n');
		return os;
	}

	template <class CharT, class Traits>
	std::basic_ostream<CharT, Traits> &ends(std::basic_ostream<CharT, Traits> &os) {
		os.put(CharT());
		return os;
	}

} // namespace std

#endif
